using Mono.Cecil;
using Mono.Cecil.Cil;

namespace Cil.Ast;

/// <summary>
/// 初始化结构体表达式
/// </summary>
public sealed class InitStructExpression : SingleInstructionExpression
{
    public const int InstancePos = 0;
    public const int MaxNodes = 1;

    public InitStructExpression(Context context, Instruction position, Expression instance, TypeReference type)
        : base(context, position)
    {
        if (instance.WritesStack != 1)
        {
            throw new InvalidOperationException($"Wrong stack items count in <exp_length> argument: {instance.WritesStack}");
        }

        if (!instance.ReturnType.IsValueType)
        {
            throw new ArgumentException($"<exp_instance> should be ValueType ({instance.ReturnType})");
        }

        if (!Cast.IsAvaliable(instance.ReturnType, type))
        {
            throw new InvalidCastException($"<exp_instance> ({instance}) type cannot be initialized as <ref_type> ({type})");
        }

        // base class
        ReadsStack = 1;
        ReturnType = Context.MakeRef(typeof(void));
        Nodes = new FixedList<Expression>(MaxNodes);

        // this
        InitAsType = type;
        Instance = instance;
    }

    public TypeReference InitAsType { get; private set; }

    public Expression Instance
    {
        get => Nodes[InstancePos];
        set
        {
            Nodes[InstancePos] = value;
            value.ParentNode = this;
        }
    }

    public override string ToString()
    {
        return $"var {Instance} = new {InitAsType}()";
    }
}

public static partial class IlAst
{
    public static InitStructExpression InitStruct(Context context, Instruction position, Expression instance, TypeReference type)
    {
        return new InitStructExpression(context, position, instance, type);
    }

    public static InitStructExpression InitStruct(Context context, Instruction position, Expression instance)
    {
        return new InitStructExpression(context, position, instance, instance.ReturnType);
    }
}